package com.lkd.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.base.Strings;
import com.lkd.common.VMSystem;
import com.lkd.config.TopicConfig;
import com.lkd.contract.SupplyChannel;
import com.lkd.contract.SupplyContract;
import com.lkd.contract.TaskCompleteContract;
import com.lkd.dao.TaskDao;
import com.lkd.emq.MqttProducer;
import com.lkd.entity.TaskDetailsEntity;
import com.lkd.entity.TaskEntity;
import com.lkd.entity.TaskTypeEntity;
import com.lkd.exception.LogicException;
import com.lkd.feign.UserService;
import com.lkd.feign.VMService;
import com.lkd.http.vo.CancelTaskViewModel;
import com.lkd.http.vo.TaskDetailsViewModel;
import com.lkd.http.vo.TaskViewModel;
import com.lkd.service.TaskDetailsService;
import com.lkd.service.TaskService;
import com.lkd.service.TaskTypeService;
import com.lkd.vo.Pager;
import com.lkd.vo.UserVO;
import com.lkd.vo.VmVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


@Service
@Slf4j
public class TaskServiceImpl extends ServiceImpl<TaskDao, TaskEntity> implements TaskService {

    @Autowired
    private UserService userService;

    @Autowired
    private VMService vmService;

    @Autowired
    private TaskTypeService taskTypeService;

    @Autowired
    private TaskDetailsService taskDetailsService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private MqttProducer mqttProducer;
    @Override
    public Pager<TaskEntity> search(Long pageIndex, Long pageSize, String innerCode, Integer userId, String taskCode, Integer status, Boolean isRepair, String start, String end) {
        Page<TaskEntity> page = new Page<>(pageIndex, pageSize);
        LambdaQueryWrapper<TaskEntity> qw = new LambdaQueryWrapper<>();
        if (!Strings.isNullOrEmpty(innerCode)) {
            qw.eq(TaskEntity::getInnerCode, innerCode);
        }
        if (userId != null && userId > 0) {
            qw.eq(TaskEntity::getUserId, userId);
        }
        if (!Strings.isNullOrEmpty(taskCode)) {
            qw.like(TaskEntity::getTaskCode, taskCode);
        }
        if (status != null && status > 0) {
            qw.eq(TaskEntity::getTaskStatus, status);
        }
        if (isRepair != null) {
            if (isRepair) {
                qw.ne(TaskEntity::getProductTypeId, VMSystem.TASK_TYPE_SUPPLY);
            } else {
                qw.eq(TaskEntity::getProductTypeId, VMSystem.TASK_TYPE_SUPPLY);
            }
        }
        if (!Strings.isNullOrEmpty(start) && !Strings.isNullOrEmpty(end)) {
            qw
                    .ge(TaskEntity::getCreateTime, LocalDate.parse(start, DateTimeFormatter.ISO_LOCAL_DATE))
                    .le(TaskEntity::getCreateTime, LocalDate.parse(end, DateTimeFormatter.ISO_LOCAL_DATE).plusDays(1));
        }
        //根据最后更新时间倒序排序
        qw.orderByDesc(TaskEntity::getUpdateTime);

        return Pager.build(this.page(page, qw));
    }

    /**
     * 创建工单
     *
     * @param taskViewModel
     * @return
     */
    @Override
    @Transactional
    public Boolean createTask(TaskViewModel taskViewModel) {
        //检查参数
        checkParam(taskViewModel);
        //核查售货机状态与工单状态是否一致
        VmVO vmInfo = getVmVO(taskViewModel);
        checkTaskType(taskViewModel, vmInfo);
        //核查该设备是否有未完成订单
        checkVmTask(taskViewModel);
        //存放数据库信息的实体类
        TaskEntity taskEntity = new TaskEntity();
        //将vo类的信息拷贝到实体类中
        BeanUtils.copyProperties(taskViewModel, taskEntity);
        //将vo类中没有的信息手动设置到实体类中
        TaskTypeEntity taskTypeEntity = getTaskTypeEntity(taskViewModel, taskEntity, vmInfo);
        //存储进入数据库
        save(taskEntity);
        if (taskTypeEntity.getType().equals(VMSystem.TASK_TYPE_SUPPLY)) {
            //存储运营工单的工单详情
            saveTaskDetails(taskViewModel, taskEntity);
        }
        updateZset(taskEntity,1);
        return true;
    }

    /**
     * 更新set排行榜数据
     * @param entity
     * @param param
     */
    private void updateZset(TaskEntity entity, int param) {
        String date = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        Long regionId = entity.getRegionId();
        String roleCode ;
        if(VMSystem.TASK_TYPE_SUPPLY.equals(entity.getProductTypeId())){
            roleCode = VMSystem.USER_OPERATOR;
        }else {
            roleCode = VMSystem.USER_REPAIRER;
        }
        String key = "taskNumSet."+date+regionId+roleCode;
        redisTemplate.opsForZSet().incrementScore(key,entity.getUserId(),param);
    }

    /**
     * 接受工单
     *
     * @param id
     * @return
     */
    @Override
    public Boolean acceptTask(Long id, Integer userId) {
        TaskEntity entity = getById(id);
        if (!entity.getTaskStatus().equals(VMSystem.TASK_STATUS_CREATE)) {
            throw new LogicException("工单状态错误,请核查");
        }
        //核查用户信息与执行人是否匹配
        checkUserInfo(userId, entity);
        //更新工单状态
        updateTaskStatus(id, VMSystem.TASK_STATUS_PROGRESS,null);
        return true;
    }

    /**
     * 完成工单
     *
     * @param id
     * @param userId
     * @return
     */
    @Override
    public Boolean complete(Long id, Integer userId) {
        TaskEntity entity = getById(id);
        Integer taskStatus = entity.getTaskStatus();
        if (taskStatus.equals(VMSystem.TASK_STATUS_FINISH) || taskStatus.equals(VMSystem.TASK_STATUS_CANCEL)) {
            throw new LogicException("工单状态错误,请核查");
        }
        //核查用户信息与执行人是否匹配
        checkUserInfo(userId, entity);
        //更新工单状态
        updateTaskStatus(id, VMSystem.TASK_STATUS_FINISH, null);
        //发送消息更改设备状态
        sendUpdateMsg(entity);
        return true;
    }

    /**
     * 发送消息更改设备状态
     * @param entity
     */
    private void sendUpdateMsg(TaskEntity entity) {
        Integer productTypeId = entity.getProductTypeId();
        if(productTypeId.equals(VMSystem.TASK_TYPE_SUPPLY)){
            String msg = getSupplyCon(entity);
            mqttProducer.send(TopicConfig.VMS_SUPPLY_TOPIC,2,msg);
        }else if(productTypeId.equals(VMSystem.TASK_TYPE_REVOKE)||productTypeId.equals(VMSystem.TASK_TYPE_DEPLOY)){
            String msg = getTaskCompleteCon(entity, productTypeId);
            mqttProducer.send(TopicConfig.VMS_COMPLETED_TOPIC,2,msg);
        }
    }

    /**
     * 封装运营工单消息实体类
     * @param entity
     * @return
     */
    private String getSupplyCon(TaskEntity entity) {
        SupplyContract supplyContract = new SupplyContract();
        supplyContract.setInnerCode(entity.getInnerCode());
        Long taskId = entity.getTaskId();
        List<TaskDetailsEntity> list = taskDetailsService.list(new LambdaQueryWrapper<TaskDetailsEntity>().eq(TaskDetailsEntity::getTaskId, taskId));
        List<SupplyChannel> list1 = new ArrayList<>();
        for (TaskDetailsEntity taskDetailsEntity : list) {
            SupplyChannel supplyChannel = new SupplyChannel();
            BeanUtils.copyProperties(taskDetailsEntity,supplyChannel);
            supplyChannel.setChannelId(taskDetailsEntity.getChannelCode());
            supplyChannel.setCapacity(taskDetailsEntity.getExpectCapacity());
            list1.add(supplyChannel);
        }
        supplyContract.setSupplyData(list1);
        return JSON.toJSONString(supplyContract);
    }

    /**
     * 封装维运维工单消息实体类
     * @param entity
     * @param productTypeId
     * @return
     */
    private String getTaskCompleteCon(TaskEntity entity, Integer productTypeId) {
        TaskCompleteContract taskCompleteContract = new TaskCompleteContract();
        taskCompleteContract.setInnerCode(entity.getInnerCode());
        taskCompleteContract.setTaskType(productTypeId);
        return JSON.toJSONString(taskCompleteContract);
    }

    /**
     * 取消工单
     *
     * @param id
     * @param userId
     * @param cancelTaskViewModel
     * @return
     */
    @Override
    public Boolean cancelTask(Long id, Integer userId, CancelTaskViewModel cancelTaskViewModel) {
        TaskEntity entity = getById(id);
        Integer taskStatus = entity.getTaskStatus();
        if (taskStatus.equals(VMSystem.TASK_STATUS_FINISH) || taskStatus.equals(VMSystem.TASK_STATUS_CANCEL)) {
            throw new LogicException("工单状态错误,请核查");
        }
        //核查用户信息与执行人是否匹配
        checkUserInfo(userId, entity);
        //更新工单状态
        updateTaskStatus(id, VMSystem.TASK_STATUS_CANCEL, cancelTaskViewModel);
        updateZset(entity,-1);
        return true;
    }

    /**
     * 更新工单状态
     *
     * @param id
     * @param taskStatus
     */
    private void updateTaskStatus(Long id, Integer taskStatus, CancelTaskViewModel cancelTaskViewModel) {
        TaskEntity taskEntity = new TaskEntity();
        taskEntity.setTaskId(id);
        taskEntity.setTaskStatus(taskStatus);
        if (null != cancelTaskViewModel && !StringUtils.isBlank(cancelTaskViewModel.getDesc())) {
            taskEntity.setDesc(cancelTaskViewModel.getDesc());
        }
        updateById(taskEntity);
    }

    /**
     * 核查用户信息与执行人是否匹配
     *
     * @param userId
     * @param entity
     */
    private void checkUserInfo(Integer userId, TaskEntity entity) {
        if (!entity.getUserId().equals(userId)) {
            throw new LogicException("当前用户不是执行人");
        }
    }

    /**
     * 核查设备工单信息
     *
     * @param taskViewModel
     */
    private void checkVmTask(TaskViewModel taskViewModel) {
        List<Integer> status = new ArrayList<>();
        status.add(VMSystem.TASK_STATUS_CREATE);
        status.add(VMSystem.TASK_STATUS_PROGRESS);
        List<TaskEntity> list = lambdaQuery().eq(TaskEntity::getInnerCode, taskViewModel.getInnerCode()).in(TaskEntity::getTaskStatus,status).list();
        if (null != list && list.size() > 0) {
            throw new LogicException("该设备有未完成订单,请等待完成或取消后再试");
        }
    }

    /**
     * 核查工单与设备状态
     *
     * @param taskViewModel
     * @param vmInfo
     */
    private void checkTaskType(TaskViewModel taskViewModel, VmVO vmInfo) {
        int productType = taskViewModel.getProductType();
        switch (vmInfo.getVmStatus()) {
            case 0:
                if (productType != VMSystem.TASK_TYPE_DEPLOY) {
                    throw new LogicException("售货机未投放,不能创建该类型工单");
                }
                break;
            case 1:
                if (productType == VMSystem.TASK_TYPE_DEPLOY) {
                    throw new LogicException("售货机运营中,不能创建该类型工单");
                }
                break;
            case 3:
                if (productType != VMSystem.TASK_TYPE_DEPLOY) {
                    throw new LogicException("售货机已撤机,不能创建该类型工单");
                }
                break;
        }
    }

    /**
     * 核查售货机编号
     *
     * @param taskViewModel
     * @return
     */
    private VmVO getVmVO(TaskViewModel taskViewModel) {
        VmVO vmInfo = vmService.getVMInfo(taskViewModel.getInnerCode());
        if (null == vmInfo) {
            throw new LogicException("售货机编号出错");
        }
        return vmInfo;
    }

    /**
     * 检查传入参数
     *
     * @param taskViewModel
     */
    private void checkParam(TaskViewModel taskViewModel) {
        //检查执行人是否为空
        if (null == taskViewModel.getUserId()) {
            throw new LogicException("请指定执行人员");
        }
        //检查设备编号是否为空
        if (StringUtils.isBlank(taskViewModel.getInnerCode())) {
            throw new LogicException("请输入设备编号");
        }
        //检查工单类型是否为空
        if (0 == taskViewModel.getProductType()) {
            throw new LogicException("请选择工单类型");
        }
    }

    /**
     * 批量存储工单详情数据
     *
     * @param taskViewModel
     * @param taskEntity
     */
    private void saveTaskDetails(TaskViewModel taskViewModel, TaskEntity taskEntity) {
        List<TaskDetailsViewModel> details = taskViewModel.getDetails();
        Map<String,Integer> map = new HashMap<>();
        for (TaskDetailsViewModel detail : details) {
            String channelCode = detail.getChannelCode();
            if(map.get(channelCode)!=null) {
                map.put(channelCode, map.get(channelCode) + 1);
            }else {
                map.put(channelCode, 1);
            }
        }
        Boolean flag = true;
        for (TaskDetailsViewModel detail : details) {
            if(map.get(detail.getChannelCode())>=2){
                log.info("前端输入数据有误");
                flag = false;
            }
        }
        if(flag == false){
            int size = details.size();
            details = details.subList(size/2,size);
        }
        List<TaskDetailsEntity> list = new ArrayList<>();
        for (TaskDetailsViewModel detail : details) {
            TaskDetailsEntity taskDetailsEntity = new TaskDetailsEntity();
            BeanUtils.copyProperties(detail, taskDetailsEntity);
            taskDetailsEntity.setTaskId(taskEntity.getTaskId());
            list.add(taskDetailsEntity);
        }
        taskDetailsService.saveBatch(list);
    }

    /**
     * 封装实体类
     *
     * @param taskViewModel
     * @param taskEntity
     * @param vmInfo
     * @return
     */
    private TaskTypeEntity getTaskTypeEntity(TaskViewModel taskViewModel, TaskEntity taskEntity, VmVO vmInfo) {
        taskEntity.setTaskCode(generateTaskcode());
        Integer userId = taskViewModel.getUserId();
        UserVO user = userService.getUser(userId);
        taskEntity.setUserName(user.getUserName());
        taskEntity.setRegionId(vmInfo.getRegionId());
        taskEntity.setAddr(vmInfo.getNodeAddr());
        taskEntity.setTaskStatus(VMSystem.TASK_STATUS_CREATE);
        int productType = taskViewModel.getProductType();
        taskEntity.setProductTypeId(productType);
        TaskTypeEntity taskTypeEntity = taskTypeService.getById(productType);
        taskEntity.setTaskType(taskTypeEntity);
        return taskTypeEntity;
    }

    /**
     * 生成工单编号
     *
     * @return
     */
    private String generateTaskcode() {
        String date = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        String key = "lkd.task.code." + date;
        Object o = redisTemplate.opsForValue().get(key);
        if (o == null) {
            redisTemplate.opsForValue().setIfAbsent(key, 1L, Duration.ofDays(1));
        }
        return date + String.format("%04d", redisTemplate.opsForValue().increment(key, 1));
    }
}
